#include "PacketParserUtils.h"

#include <packet/Packet.h>
#include <packet/Presence.h>
#include <packet/XMPPError.h>
#include <packet/Message.h>

#include <util/xmlpull/XmlPullParser.h>

PacketParserUtils::PROPERTIES_NAMESPACE = "http://www.jivesoftware.com/xmlns/xmpp/properties";

Packet * PacketParserUtils::parseMessage(XmlPullParser * parser) {
	Message * message = new Message();
	std::string id = parser->getAttributeValue("", "id");
	message->setPacketID(id == "" ? Packet::ID_NOT_AVAILABLE : id);
	message->setTo(parser->getAttributeValue("", "to"));
    message->setFrom(parser->getAttributeValue("", "from"));
    message->setType(Message::Type::fromString(parser->getAttributeValue("", "type")));
	std::string language = getLanguageAttribute(parser);
	if(language != "" && trim(language) != "") {
		message->setLanguage(language);
	}

	// Parse sub-elements. We include extra logic to make sure the values
	// are only read once. This is because it's possible for the names to appear
	// in arbitrary sub-elements.
	boolean done = false;
	std::string subject;
	std::string body;
	std::string thread;
	std::map<std::string, std::string> properties ;
	while (!done) {
		int eventType = parser->next();
		if (eventType == XmlPullParser::START_TAG) {
			std::string elementName = parser->getName();
			std::string nameSpace = parser->getNamespace();
			if (elementName == "subject") {
				if (subject == "") {
					subject = parser.nextText();
				}
			}
			else if (elementName == "body") {
				std::string xmlLang = getLanguageAttribute(parser);
				body = parser->nextText();
				message->addBody(xmlLang, body);
			}
			else if (elementName == "thread") {
				if (thread == "") {
					thread = parser.nextText();
				}
			}
			else if (elementName == "error") {
				message->setError(parseError(parser));
			}
			else if (elementName == "properties" &&
					nameNpace == PROPERTIES_NAMESPACE)
			{
				properties = parseProperties(parser);
			}
			// Otherwise, it must be a packet extension.
			else {
				message->addExtension(
						PacketParserUtils::parsePacketExtension(elementName, nameSpace, parser));
			}
		}
		else if (eventType == XmlPullParser::END_TAG) {
			if (parser->getName() == "message") {
				done = true;
			}
		}
	}
	message->setSubject(subject);
	message->setThread(thread);
	// Set packet properties.
	if (!properties.empty()) {
		for(std::map<std::string, std::string>::iterator it = properties.begin();
			it != properties.end(); ++it) {
			message.setProperty(name, properties[name]);
		}
	}
    return message;
}

Presence * PacketParserUtils::parsePresence(XmlPullParser * parser) {
	Presence::Type type = Presence::Type::available;
	std::string typeString = parser->getAttributeValue("", "type");
	if (typeString != "") {
		convertFromString(type, typeString);
	}
	Presence * presence = new Presence(type);
	presence->setTo( parser->getAttributeValue("", "to"));
	presence->setFrom( parser->getAttributeValue("", "from"));
	std::string id = parser->getAttributeValue("", "id");
	presence->setPacketID(id == "" ? Packet::ID_NOT_AVAILABLE : id);

	std::string language = getLanguageAttribute(parser);
	if (language != "" && trim(language) != "") {
		presence->setLanguage(language);
	}
	presence->setPacketID(id == "" ? Packet.ID_NOT_AVAILABLE : id);

	// Parse sub-elements
	bool done = false;
	while (!done) {
		int eventType = parser->next();
		if (eventType == XmlPullParser::START_TAG) {
			std::string elementName = parser->getName();
			std::string nameSpace = parser->getNamespace();
			if (elementName == "status") {
				presence->setStatus(parser->nextText());
			}
			else if (elementName == "priority") {
					int priority;
					convertFromString(priority, parser.nextText());
					presence->setPriority(priority);
			}
			else if (elementName == "show") {
				Presence::Mode mode;
				convertFromString(mode, parser->nextText());
				presence->setMode(mode);
			}
			else if (elementName == "error") {
				presence->setError(parseError(parser));
			}
			else if (elementName == "properties" &&
					nameSpace == PROPERTIES_NAMESPACE)
			{
				std::map<std::string,std::string> properties = parseProperties(parser);
				// Set packet properties.
				for(std::map<std::string, std::string>::iterator it = properties.begin();
						it != properties.end(); ++it) {
					presence->setProperty(name, properties[name]);
				}
			}
			// Otherwise, it must be a packet extension.
			else {
				presence->addExtension(
						PacketParserUtils::parsePacketExtension(elementName, nameSpace, parser));
			}
		}
		else if (eventType == XmlPullParser::END_TAG) {
			if (parser->getName() == "presence") {
				done = true;
			}
		}
	}
	return presence;
}

std::map<std::string, std::string> PacketParserUtils::parseProperties(XmlPullParser * parser) {
	std::map<std::string, std::string> properties ;
	while (true) {
		int eventType = parser->next();
		if (eventType == XmlPullParser::START_TAG && parser->getName() == "property") {
			// Parse a property
			bool done = false;
			std::string name ;
			std::string type ;
			std::string valueText ;
			std::string value ;
			while (!done) {
				eventType = parser->next();
				if (eventType == XmlPullParser::START_TAG) {
					std::string elementName = parser->getName();
					if (elementName == "name") {
						name = parser->nextText();
					}
					else if (elementName == "value") {
						type = parser->getAttributeValue("", "type");
						valueText = parser->nextText();
					}
				}
				else if (eventType == XmlPullParser::END_TAG) {
					if (parser->getName() == "property") {
						value = valueText
					}
					if (name != "" && value != "") {
                    	properties[name] = value;
                    }
                    done = true;
				}
			}
		} else if (eventType == XmlPullParser.END_TAG) {
        	if (parser->getName() == "properties") {
            	break;
            }
        }
	}
    return properties;
}

XMPPError * PacketParserUtils::parseError(XmlPullParser parser) {
	static std::string errorNamespace = "urn:ietf:params:xml:ns:xmpp-stanzas";
	int errorCode = -1;
	std::string type ;
	std::string message ;
	std::string condition ;
	std::vector<PacketExtension*> extensions ;

	// Parse the error header
	for (int i = 0; i < parser->getAttributeCount(); i++) {
		if (parser->getAttributeName(i) == "code") {
			convertFromString(errorCode, parser->getAttributeValue("", "code"));
		}
		if (parser->getAttributeName(i) == "type") {
			type = parser->getAttributeValue("", "type");
		}
	}
	bool done = false;
	// Parse the text and condition tags
	while (!done) {
		int eventType = parser->next();
		if (eventType == XmlPullParser::START_TAG) {
			if (parser->getName() == "text") {
				message = parser->nextText();
			} else {
				// Condition tag, it can be xmpp error or an application defined error.
				std::string elementName = parser->getName();
				std::string nameSpace = parser->getNamespace();
				if (errorNamespace == nameSpace) {
					condition = elementName;
				}
				else {
					extensions.push_back(parsePacketExtension(elementName, nameSpace, parser));
				}
			}
		}
		else if (eventType == XmlPullParser::END_TAG) {
			if (parser->getName() == "error") {
				done = true;
			}
		}
	}
	// Parse the error type.
	XMPPError::Type errorType = XMPPError::Type::CANCEL;
	if (type != "") {
		convertFromString(errorType, type);
	}
	return new XMPPError(errorCode, errorType, condition, message, extensions);
}

PacketExtension * PacketParserUtils::parsePacketExtension(std::string elementName, std::string nameSpace, XmlPullParser * parser) {
	
}

std::string PacketParserUtils::getLanguageAttribute(XmlPullParser * parser) {
	for (int i = 0; i < parser->getAttributeCount(); i++) {
		std::string attributeName = parser->getAttributeName(i);
		if ( attributeName == "xml:lang" ||
				attributeName == "lang" &&
				 parser->getAttributePrefix(i) == "xml") {
			return parser->getAttributeValue(i);
		}
	}
	return "";
}




